Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There are two parts to this pull request.
Visuals
Firstly we have an implementation of visuals for arena, which allows you to do horrific things like this.
The visual objects and the means of styling them are largely the same in arena as world, so I've been able to bring over the style structs from the rust world api, such as TextStyle, PolyStyle and CircleStyle. However the underlying API is a bit different so we can't serialise the visuals on the rust side then send them over to javascript in a single call (as the rust world api does); instead we have to keep switching between rust and js as we build up the objects.
A challenge with the design of this interface has been how to handle the specification of positions of the visuals. They're typically described in the game documentation as
May be GameObject or any object containing x and y properties
. Duck typing is not available in the rust type system, so I've considered two ways to pass these parameters.pub fn circle(this: &Visual, pos: &JsValue, style: &JsValue)
and rely on the caller to do a serde conversion from whatever struct they want. This is close to the javascript semantics, but it essentially eliminates type checking, and why would you be writing rust code if you didn't like your code to be type-checked?struct VisualPosition {x: f32, y:f32}
alongside helper traits to obtain a VisualPosition from a GameObject. This means the api method can be type-checked:pub fn circle(self: &Visual, pos: &VisualPosition, style: Option<&CircleStyle>)
.This lets us do
VisualPosition::from(&creep)
. But sometimes we're not dealing with GameObjects - perhaps a desired future attack position, or perhaps we're interested in the path a creep will be taking. So I've also added VisualPosition::from() for Position. This leads onto part 2 of this change, which is an improved Position struct from pathfinder. I've added a pos() method to GameObjects, which returns a Position.Position
The second part of this change lets us obtain pathfinder's Position struct from every GameObject via a pos() method, and the Position struct is augmented with PartialEq and Hash traits that allow use to do useful things with Position, and I've added a
From<Position> for JsValue
so that it can be easily converted into JsValue for bindgen api functions.I've added Add for Position trait, as well as checked_add_direction() and saturating_add_direction(), to keep commonality with the world api.
Note that Position contains u8 x/y coordinates that correspond to coordinates in the game world, while VisualPosition contains floating point x/y coordinates as visuals can be placed at fractional coordinate locations.
This Position work is partly here because it works nicely with the visual api methods, and partly because being able to use Position throughout your bot code is a useful alternative to x/y.
I don't think this Position work is too opinionated: Ranamar on discord has done some similar changes to his copy of the API so other people probably have too, adding pos() makes better use of an existing structure (rather than inventing something new), and it's a usage pattern familiar from world and the world API. So I don't think the Position commit is a big deal, but if it's seen as undesirable it could be separated from the Visual commit (I've kept the two commits largely independent).
Demo Code
I've created a function to test the API, and it generated the visuals in the image above. I'll include it here as I think it's useful for showing how the new methods can be used.
edit: updated to remove the stuff about AsRef, that wasn't needed for VisualPosition::from() of GameObject derived types.